<template>
  <div ref="BaseForm" class="BaseForm">
    <YhForm
      ref="BaseFormRef"
      inline
      :model="formData"
      :rules="rules"
      :scroll-to-error="scrollToError"
      :class="'colCount' + colCount"
    >
      <YhFormItem
        v-for="item in columns"
        :key="item.prop"
        :class="item.prop"
        :label="item.label"
        :prop="item.prop"
        :span="calculateSpan(item)"
        :label-width="labelWidth"
        :max="item.max"
        :min="item.min"
        :valueType="item.valueType"
        :cancelValueTypeValidator="item.cancelValueTypeValidator"
        :required="item.required"
        :append="item.append"
        @keydown.exact="keydown($event, item.prop as string)"
      >
        <YhTimePicker
          v-if="item.type === 'time'"
          v-model="formData[item.prop!]"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :format="item.format ? item.format : 'HH:mm:ss'"
          :value-format="item.format ? item.format : 'HH:mm:ss'"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请选择' + item.label
          "
          @change="change('time', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhTimePicker>
        <YhTimePicker
          v-if="item.type === 'timeRange'"
          v-model="formData[item.prop!]"
          is-range
          range-separator="-"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :format="item.format ? item.format : 'HH:mm:ss'"
          :value-format="item.format ? item.format : 'HH:mm:ss'"
          :start-placeholder="
            item['startPlaceholder'] !== undefined
              ? item['startPlaceholder']
              : item.disabled
                ? ''
                : '起始时间'
          "
          :end-placeholder="
            item['endPlaceholder'] !== undefined
              ? item['endPlaceholder']
              : item.disabled
                ? ''
                : '结束时间'
          "
          @change="change('timeRange', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhTimePicker>
        <YhDatePicker
          v-if="item.type === 'date'"
          v-model="formData[item.prop!]"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :format="item.format ? item.format : 'YYYY-MM-DD'"
          :value-format="item.format ? item.format : 'YYYY-MM-DD'"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请选择' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('date', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhDatePicker>
        <YhDatePicker
          v-if="item.type === 'dateTime'"
          v-model="formData[item.prop!]"
          type="datetime"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :format="item.format ? item.format : 'YYYY-MM-DD HH:mm:ss'"
          :value-format="item.format ? item.format : 'YYYY-MM-DD HH:mm:ss'"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请选择日期和时间'
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('dateTime', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhDatePicker>
        <YhDatePicker
          v-if="item.type === 'dateRange'"
          v-model="formData[item.prop!]"
          type="daterange"
          range-separator="-"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :format="item.format ? item.format : 'YYYY-MM-DD'"
          :value-format="item.format ? item.format : 'YYYY-MM-DD'"
          :start-placeholder="
            item['startPlaceholder'] !== undefined
              ? item['startPlaceholder']
              : item.disabled
                ? ''
                : '起始日期'
          "
          :end-placeholder="
            item['endPlaceholder'] !== undefined
              ? item['endPlaceholder']
              : item.disabled
                ? ''
                : '结束日期'
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('dateRange', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhDatePicker>
        <YhDatePicker
          v-if="item.type === 'dateTimeRange'"
          v-model="formData[item.prop!]"
          type="datetimerange"
          range-separator="-"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :format="item.format ? item.format : 'YYYY-MM-DD HH:mm:ss'"
          :value-format="item.format ? item.format : 'YYYY-MM-DD HH:mm:ss'"
          :start-placeholder="
            item['startPlaceholder'] !== undefined
              ? item['startPlaceholder']
              : item.disabled
                ? ''
                : '起始日期'
          "
          :end-placeholder="
            item['endPlaceholder'] !== undefined
              ? item['endPlaceholder']
              : item.disabled
                ? ''
                : '结束日期'
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('dateTimeRange', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhDatePicker>

        <el-radio-group
          v-if="item.type === 'radio'"
          v-model="formData[item.prop!]"
          :disabled="disabledForm || item.disabled"
          @change="change('radio', item.prop!, $event)"
          v-on="item.events"
          v-bind="{
            ...item.props,
          }"
        >
          <el-radio
            v-for="option in item.options"
            :key="option"
            :label="option.value"
          >
            {{ option.label }}
          </el-radio>
        </el-radio-group>
        <YhSelect
          v-if="item.type === 'select'"
          v-model="formData[item.prop!]"
          :options="item.options"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请选择' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('select', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        />
        <YhSelect
          v-if="item.type === 'multipleSelect'"
          v-model="formData[item.prop!]"
          multiple
          collapse-tags
          :options="item.options"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请选择' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('multipleSelect', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        >
          <el-option
            v-for="option in item.options"
            :key="option"
            :label="option.label"
            :value="option.value"
          ></el-option>
        </YhSelect>
        <YhSelect
          v-if="item.type === 'filterable'"
          v-model="formData[item.prop!]"
          remote
          filterable
          :remote-method="item['remoteMethod']"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请选择' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('filterable', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        >
          <el-option
            v-for="option in item.options"
            :key="option"
            :label="option.label"
            :value="option.value"
          ></el-option>
        </YhSelect>
        <YhCascader
          v-if="item.type === 'cascader'"
          v-model="formData[item.prop!]"
          :options="item.options!"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请选择' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @change="change('cascader', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        />

        <YhInputNumber
          v-if="item.type === 'number'"
          v-model="formData[item.prop!]"
          :key="formData"
          :is-number="item.inputStyle"
          :precision="item.precision"
          :step="item.step"
          :max="item.max"
          :min="item.min"
          :controls="item.controls === undefined ? false : item.controls"
          :disabled="disabledForm || item.disabled"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请输入' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @input="handlerInput('number', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhInputNumber>
        <YhInput
          v-if="item.type === 'textarea'"
          v-model="formData[item.prop!]"
          type="textarea"
          show-word-limit
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :maxlength="item.max ? item.max : ''"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请输入' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @input="handlerInput('textarea', item.prop!, $event)"
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        ></YhInput>
        <YhInput
          v-if="item.type === undefined || item.type === 'input'"
          v-model="formData[item.prop!]"
          :disabled="disabledForm || item.disabled"
          :clearable="item.clearable === undefined ? true : item.clearable"
          :maxlength="item.max ? item.max : ''"
          :placeholder="
            item.placeholder !== undefined
              ? item.placeholder
              : item.disabled
                ? ''
                : '请输入' + item.label
          "
          @focus="focus(item.prop!)"
          @click="click(item.prop!)"
          @input="handlerInput('input', item.prop!, $event)"
          @blur="
            spaceClear
              ? (formData[item.prop!] = formData[item.prop!]?.trim())
              : null
          "
          v-on="item.events || {}"
          v-bind="{
            ...item.props,
          }"
        >
          <template v-if="item.prepend" #prepend>{{ item.prepend }}</template>
          <template v-if="item.append" #append>{{ item.append }}</template>
          <template #prefix>
            <slot :name="`${item.prop}-prefix`"></slot>
          </template>
          <template #suffix>
            <slot :name="`${item.prop}-suffix`"></slot>
          </template>
        </YhInput>
        <div class="custom-item-content" v-if="item.type === 'slot'">
          <slot :name="item.prop" :data="formData"></slot>
        </div>
      </YhFormItem>
    </YhForm>
  </div>
</template>

<script lang="ts" setup>
import type {
  BaseFormColumns,
  BaseFormColumnsType,
  BaseFormReference,
  FormColumnsItem,
} from "@/components/BaseForm/BaseForm.type";

import type { FormInstance } from "element-plus/es/components/form";
import type { PropType } from "vue";
import {
  YhCascader,
  YhDatePicker,
  YhForm,
  YhFormItem,
  YhInput,
  YhInputNumber,
  YhSelect,
  YhTimePicker,
} from "yh-ui";

const props = defineProps({
  // 表单字段配置
  columns: {
    type: Array as PropType<BaseFormColumns>,
    default: () => [],
  },
  // 表单label宽度
  labelWidth: {
    type: [Number, String],
    default: 80,
  },
  // 表单列数
  colCount: {
    type: Number as PropType<1 | 2 | 3 | 4>,
    default: 3,
  },
  // 禁用表单
  disabledForm: {
    type: Boolean,
    default: false,
  },
  // 跳转第一个校验错误表单
  scrollToError: {
    type: Boolean,
    default: true,
  },
  // 文本框自动处理前后空格
  spaceClear: {
    type: Boolean,
    default: true,
  },
  // 重置表单后重新填入默认值
  resetLoadDefaultValue: {
    type: Boolean,
    default: true,
  },
});

// 表单数据
const formData = ref<any>({});
// 容器实例
const BaseForm = ref<HTMLElement>();
// 表单实例
const BaseFormRef = ref<FormInstance>();

// 动态表单列数
const dynamicColCount = ref(props.colCount);
// 计算表单项span
const calculateSpan = computed(() => {
  return (item: FormColumnsItem) => {
    return item.span ? item.span : 24 / dynamicColCount.value;
  };
});
// 监听窗口大小
const listenerResize = () => {
  // 4 > 1280
  // 3 > 992
  // 2 <= 992
  if (BaseForm.value?.offsetWidth) {
    switch (true) {
      case BaseForm.value.offsetWidth > 1280:
        dynamicColCount.value = 4;
        break;
      case BaseForm.value.offsetWidth > 992:
        dynamicColCount.value = 3;
        break;
      case BaseForm.value.offsetWidth <= 992:
        dynamicColCount.value = 2;
        break;
      default:
    }

    if (dynamicColCount.value > props.colCount) {
      dynamicColCount.value = props.colCount;
    }
  }
};
// 整合每个表单项的正则
const rules = computed(() => {
  const rules: any = {};
  props.columns.forEach((item: FormColumnsItem) => {
    if (item.rules) {
      rules[item.prop || ""] = [];

      item.rules.forEach((e) => {
        if (item.type === "number" && e.max !== undefined) {
          rules[item.prop || ""].push({
            validator: (rule: any, value: any, callback: any) => {
              const data = value === undefined ? "" : `${value}`;
              if (!data) return callback();
              if (+data > +e.max!) {
                callback(new Error(e.message));
              }
              callback();
            },
            trigger: "blur",
          });
        } else if (item.type === "number" && e.min !== undefined) {
          rules[item.prop || ""].push({
            validator: (rule: any, value: any, callback: any) => {
              const data = value === undefined ? "" : `${value}`;
              if (!data) return callback();
              if (+data < +e.min!) {
                callback(new Error(e.message));
              }
              callback();
            },
            trigger: "blur",
          });
        } else {
          rules[item.prop || ""].push(e);
        }
      });
    }
  });
  return rules;
});

// 触发事件
const emits = defineEmits([
  "change",
  "formValidate",
  "numberChange",
  "radioChange",
  "selectChange",
  "uploadChange",
  "cascaderChange",
  "rangeDateAndTimepickerChange",
  "handlerInput",
  "focus",
  "click",
  "mounted",
  "keydown",
]);
const handlerInput = (type: BaseFormColumnsType, prop: string, event: any) => {
  emits("handlerInput", {
    data: event,
    prop,
  });
  if (type === "input") {
    change("input", prop, event);
  }
  if (type === "number") {
    change("number", prop, event);
  }
  if (type === "textarea") {
    change("textarea", prop, event);
  }
};
const focus = (prop: string) => {
  emits("focus", {
    prop,
    activeElement: document.activeElement,
  });
};
const click = (prop: string) => {
  emits("click", prop);
};
const change = (type: BaseFormColumnsType, prop: string, event: any) => {
  if (["timeRange", "dateRange", "dateTimeRange"].includes(type)) {
    const columnsItem = props.columns?.find((item) => item.prop === prop);
    if (event) {
      formData.value[columnsItem?.firstProp!] = event[0];
      formData.value[columnsItem?.secondProp!] = event[1];
    } else {
      delete formData.value[columnsItem?.firstProp!];
      delete formData.value[columnsItem?.secondProp!];
    }
    emits("rangeDateAndTimepickerChange", {
      data: event,
      prop,
    });
  }
  if (type === "radio") {
    emits("radioChange", {
      data: event,
      prop,
    });
  }
  if (["select", "multipleSelect", "filterable"].includes(type)) {
    emits("selectChange", {
      data: event,
      prop,
    });
  }
  if (type === "cascader") {
    emits("cascaderChange", {
      data: event,
      prop,
    });
  }
  if (type === "number") {
    emits("numberChange", {
      data: event,
      prop,
    });
  }

  emits("change", {
    prop,
    type,
    data: event,
    form: formData.value,
  });
};
const keydown = (e: any, prop: string) => {
  if (e.key === "Enter") {
    e.preventDefault();
    formData.value[prop] = (formData.value[prop] || "").trim();
  }
  emits("keydown", {
    key: e.key,
    prop,
  });
};

// 清除某个表单的校验信息
const clearValidate = (prop: string) => {
  setTimeout(() => BaseFormRef.value?.clearValidate(prop), 0);
};
// 立刻检验表单
const runValidate = async () => {
  let data = {};
  await BaseFormRef.value?.validate((valid) => {
    data = {
      valid,
      data: formData.value,
    };
    emits("formValidate", data);
  });
  return data;
};
// 修改表单某项数据
const updateFormData = (prop: string, value: any) => {
  formData.value[prop] = value;
};
// 修改表单所有数据
const updateForm = (data: any) => {
  formData.value = data;
};
// 重置表单
const reset = () => {
  BaseFormRef.value?.resetFields();
  formData.value = {};
  if (props.resetLoadDefaultValue) {
    // 给表单绑定默认值
    props.columns?.forEach((item) => {
      item.defaultValue !== undefined
        ? (formData.value[item.prop || ""] = item.defaultValue)
        : null;
    });
  }
};

onMounted(() => {
  // 给表单绑定默认值
  props.columns?.forEach((item) => {
    item.defaultValue !== undefined
      ? (formData.value[item.prop || ""] = item.defaultValue)
      : null;
  });
  addEventListener("resize", listenerResize);

  emits("mounted");
});
onUnmounted(() => {
  removeEventListener("resize", listenerResize);
});

// 暴露出组件的方法或属性
defineExpose<BaseFormReference>({
  clearValidate,
  updateFormData,
  updateForm,
  reset,
  runValidate,
  formData,
});
</script>

<style scoped lang="scss">
.BaseForm {
  :deep(.el-form) {
    .el-form-item {
      width: 100%;

      .yh-avatar-uploader {
        ul {
          .el-upload {
            transition:
              all 0.3s,
              transform 0.5s;
            margin-bottom: 9px;

            i {
              height: 100%;
            }
          }
        }
      }

      .yh-has-append {
        & > div:first-of-type {
          flex: 1;
        }
      }

      .el-input-number {
        vertical-align: baseline;

        .el-input__inner {
          text-align: left;
        }
      }

      .custom-item-content {
        width: 100%;
      }
    }
  }
}
</style>
